#include "DX10.h"
#include "Camera.h"
#include "TargetBox.h"
#include "LaserRangefinder.h"
#include "Field.h"
#include "Grass.h"
#include "CBallisticCalculator.h"
#include "PhysicsObject.h"

CRunManager *RM;
CDXInput *Input;

CBallisticCalculator *Calc;

class Application : public DX10
{
	CTargetBox mBox;
	CLaserRangefinder *mLaser;
	CField mField;


	ID3D10Effect* mFX;
	ID3D10EffectTechnique* mTech;
	ID3D10InputLayout* mVertexLayout;
	ID3D10EffectMatrixVariable* mfxWVPVar;

	D3DXMATRIX mView;
	D3DXMATRIX mProj;
	D3DXMATRIX mWVP;

	float mTheta;
	float mPhi;

public:
	Application(HINSTANCE hInstance);
	~Application();

	CGrass *mGrass;

	void initApp();
	void onResize();
	void updateScene(float dt);
	void drawScene();
	void FPS();

	void buildFX();
	void buildVertexLayouts();
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
				   PSTR cmdLine, int showCmd)
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif


	Application theApp(hInstance);
	
	theApp.initApp();

	RM = CRunManager::GetInstance();

	Input = CDXInput::GetInstance();

	theApp.mGrass = CGrass::GetInstance();

	Calc = CBallisticCalculator::GetInstance();

	return theApp.run();
}

Application::Application(HINSTANCE hInstance) : DX10(hInstance), mFX(0), mTech(0), mVertexLayout(0),
  mfxWVPVar(0), mTheta(0.0f), mPhi(M_PI*0.25f)
{
	D3DXMatrixIdentity(&mView);
	D3DXMatrixIdentity(&mProj);
	D3DXMatrixIdentity(&mWVP);
}

Application::~Application()
{
	if( md3dDevice )
		md3dDevice->ClearState();

	ReleaseCOM(mFX);
	ReleaseCOM(mVertexLayout);

	RM->CountRun();
}

void Application::initApp()
{
	DX10::initApp();

	Input = CDXInput::GetInstance();

	Input->InitDevices(this->getMainWnd(), this->getAppInst());

	mBox.init(md3dDevice, 1.0f);

	mField.init(md3dDevice, 1.0f);

	mGrass->GetInstance()->init(md3dDevice);

	mLaser = CLaserRangefinder::GetInstance();

	mLaser->init(md3dDevice, RED);

	buildFX();
	buildVertexLayouts();
}

void Application::onResize()
{
	DX10::onResize();

	float aspect = (float)mClientWidth/mClientHeight;
	D3DXMatrixPerspectiveFovLH(&mProj, 0.25f*M_PI, aspect, 1.0f, 7500.0f);
}

void Application::updateScene(float dt)
{
	DX10::updateScene(dt);

	Input->ReadDevices();

	CCamera::GetInstance()->CameraMovement(dt);

	this->mView = CCamera::GetInstance()->Update(dt);

	if(GetAsyncKeyState('O') & 0x8000)
	{
		mBox.SetPosition(D3DXVECTOR3(0.0f, 5.0f, 0.0f));
		
	}
	else
	{
		mBox.SetPosition(D3DXVECTOR3(0.0f, 5.0f, 2000.0f));
	}

	if(GetAsyncKeyState('P') & 0x8000)
	{
		mBox.init(md3dDevice, 2.0f); 
	}
	else
	{
		mBox.init(md3dDevice, 1.0f);
	}

	mLaser->GetDistance(D3DXVECTOR3(0.0f, 5.0f, 0.0f), D3DXVECTOR3(0.0f, 5.0f, 2000.0f));
	mLaser->CalculateLaser();
	mLaser->init(md3dDevice, ORANGE);

	DragModelData val;

	val = Calc->Retard(G2, 0.945, 1500);

}

void Application::FPS()
{
	// We specify DT_NOCLIP, so we do not care about width/height of the rect.
	RECT R = {5, 5, 0, 0};

	if(DX10::mTimer.GetFPS() > 60)
	{
		mFont->DrawText(0, mFrameStats.c_str(), -1, &R, DT_NOCLIP, DARKGREEN);
	}

	else if(DX10::mTimer.GetFPS() > 30)
	{
		mFont->DrawText(0, mFrameStats.c_str(), -1, &R, DT_NOCLIP, YELLOW);
	}

	else
	{
		mFont->DrawText(0, mFrameStats.c_str(), -1, &R, DT_NOCLIP, RED);
	}
}

void Application::drawScene()
{
	DX10::drawScene();

	// Restore default states, input layout and primitive topology 
	// because mFont->DrawText changes them.  Note that we can 
	// restore the default states by passing null.
	md3dDevice->OMSetDepthStencilState(0, 0);
	float blendFactors[] = {0.0f, 0.0f, 0.0f, 0.0f};
	md3dDevice->OMSetBlendState(0, blendFactors, 0xffffffff);
    md3dDevice->IASetInputLayout(mVertexLayout);
    md3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// set constants
	mWVP = mView*mProj;
	mfxWVPVar->SetMatrix((float*)&mWVP);

    D3D10_TECHNIQUE_DESC techDesc;
    mTech->GetDesc( &techDesc );
    for(UINT p = 0; p < techDesc.Passes; ++p)
    {
        mTech->GetPassByIndex( p )->Apply(0);
        
		mField.draw();

		mBox.draw();

		mLaser->draw();

		mGrass->draw();
    }

	FPS();

	mSwapChain->Present(0, 0);
}

void Application::buildFX()
{
	DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    shaderFlags |= D3D10_SHADER_DEBUG;
	shaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
#endif
 
	ID3D10Blob* compilationErrors = 0;
	HRESULT hr = 0;
	hr = D3DX10CreateEffectFromFile(L"color.fx", 0, 0, 
		"fx_4_0", shaderFlags, 0, md3dDevice, 0, 0, &mFX, &compilationErrors, 0);
	if(FAILED(hr))
	{
		if( compilationErrors )
		{
			MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0);
			ReleaseCOM(compilationErrors);
		}
		DXTrace(__FILE__, (DWORD)__LINE__, hr, L"D3DX10CreateEffectFromFile", true);
	} 

	mTech = mFX->GetTechniqueByName("ColorTech");
	
	mfxWVPVar = mFX->GetVariableByName("gWVP")->AsMatrix();
}

void Application::buildVertexLayouts()
{
	// Create the vertex input layout.
	D3D10_INPUT_ELEMENT_DESC vertexDesc[] =
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
		{"COLOR",    0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}
	};

	// Create the input layout
    D3D10_PASS_DESC PassDesc;
    mTech->GetPassByIndex(0)->GetDesc(&PassDesc);
    HR(md3dDevice->CreateInputLayout(vertexDesc, 2, PassDesc.pIAInputSignature,
		PassDesc.IAInputSignatureSize, &mVertexLayout));
}